/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus

This file is part of Tremulous.

Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Tremulous is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

#include "g_local.h"

//ROTAX
void kill_aliens( void );
void ambush_next_stage( void );

damageRegion_t  g_damageRegions[ PCL_NUM_CLASSES ][ MAX_LOCDAMAGE_REGIONS ];
int             g_numDamageRegions[ PCL_NUM_CLASSES ];

armourRegion_t  g_armourRegions[ UP_NUM_UPGRADES ][ MAX_ARMOUR_REGIONS ];
int             g_numArmourRegions[ UP_NUM_UPGRADES ];

/*
============
AddScore

Adds score to both the client and his team
============
*/
void AddScore( gentity_t *ent, int score )
{
  if( !ent->client )
    return;

  ent->client->ps.persistant[ PERS_SCORE ] += score;
  CalculateRanks( );
}

/*
==================
LookAtKiller
==================
*/
void LookAtKiller( gentity_t *self, gentity_t *inflictor, gentity_t *attacker )
{
  vec3_t    dir;

  if ( attacker && attacker != self )
    VectorSubtract( attacker->s.pos.trBase, self->s.pos.trBase, dir );
  else if( inflictor && inflictor != self )
    VectorSubtract( inflictor->s.pos.trBase, self->s.pos.trBase, dir );
  else
  {
    self->client->ps.stats[ STAT_VIEWLOCK ] = self->s.angles[ YAW ];
    return;
  }

  self->client->ps.stats[ STAT_VIEWLOCK ] = vectoyaw( dir );
}

// these are just for logging, the client prints its own messages
char *modNames[ ] =
{
  "MOD_UNKNOWN",
  "MOD_SHOTGUN",
  "MOD_BLASTER",
  "MOD_PAINSAW",
  "MOD_MACHINEGUN",
  "MOD_CHAINGUN",
  "MOD_PRIFLE",
  "MOD_MDRIVER",
  "MOD_LASGUN",
  "MOD_LCANNON",
  "MOD_LCANNON_SPLASH",
  "MOD_FLAMER",
  "MOD_FLAMER_SPLASH",
  "MOD_GRENADE",
  "MOD_WATER",
  "MOD_SLIME",
  "MOD_LAVA",
  "MOD_CRUSH",
  "MOD_TELEFRAG",
  "MOD_FALLING",
  "MOD_SUICIDE",
  "MOD_TARGET_LASER",
  "MOD_TRIGGER_HURT",

  "MOD_ABUILDER_CLAW",
  "MOD_LEVEL0_BITE",
  "MOD_LEVEL1_CLAW",
  "MOD_LEVEL1_PCLOUD",
  "MOD_LEVEL3_CLAW",
  "MOD_LEVEL3_POUNCE",
  "MOD_LEVEL3_BOUNCEBALL",
  "MOD_LEVEL2_CLAW",
  "MOD_LEVEL2_ZAP",
  "MOD_LEVEL4_CLAW",
  "MOD_LEVEL4_CHARGE",

  "MOD_SLOWBLOB",
  "MOD_POISON",
  "MOD_SWARM",

  "MOD_HSPAWN",
  "MOD_TESLAGEN",
  "MOD_MGTURRET",
  "MOD_REACTOR",

  "MOD_ASPAWN",
  "MOD_ATUBE",
  "MOD_OVERMIND",
  "MOD_RADIATION",
  "MOD_SLAP",
  "MOD_EXPLODE",
  "MOD_POOP",
  "MOD_LEVEL4_CRUSH"
};

/*
=============
Print_Obituary
=============
*/
void Print_Obituary( gentity_t *self, gentity_t *attacker, int mod , qboolean  tk)
{
 // int           mod;
  int           killer;
  float                 randomness;
  char          *message;
  char          *message2;
  //const char    *targetInfo;
  //const char    *attackerInfo;
  char          targetName[ 32 ];
  char          attackerName[ 32 ];
  char          className[ 64 ];
  int      gender = self->client->pers.gender;
  //clientInfo_t  *ci;
  //qboolean      teamKill = qfalse;

  Q_strncpyz( targetName, self->client->pers.netname, sizeof( targetName ) - 2 );
  strcat( targetName, S_COLOR_WHITE );

  message2 = "";

        if( attacker )
          {
            killer = attacker->s.number;        
            if( attacker->client )
                        Q_strncpyz( attackerName, attacker->client->pers.netname, sizeof( attackerName ) - 2 );
              //attackerName = attacker->client->pers.netname;
            else
                Q_strncpyz( attackerName, "<non-client>", sizeof( attackerName ) - 2 );
              //attackerName = "<non-client>";
            strcat( attackerName, S_COLOR_WHITE );
        }
        else
        {
          killer = ENTITYNUM_WORLD;
          Q_strncpyz( attackerName, "<world>", sizeof( attackerName ) - 2 );
          //attackerName = "<world>";
        }

        //add some randomness so that we can have varied messages
  randomness = random();
  // check for single client messages
  switch( mod )
  {
              case MOD_FALLING:
                if(randomness < 0.2)
                        message = "fell fowl to gravity";
                else if(randomness < 0.3)
                        message = "looks like the contents of a Big Mac";
                else if(randomness < 0.4)
                        message = "is now a doormat";
                else if(randomness < 0.6)
                        message = "blames Newton";
                else if(randomness < 0.8)
                        message = "rediscovered gravity";
                else
                        message = "forgot to take a jetpack";
                break;
              case MOD_CRUSH:
                if(randomness < 0.5)
                  message = "was squished";
                else
                  message = "was turned into Pate";
                break;
              case MOD_WATER:
                if(randomness < 0.3)
                  message = "forgot to pack a snorkel";
                else if(randomness < 0.5) {
                  if(gender == GENDER_FEMALE)
                          message = "thought she was a fish, and was mistaken";
                  else if(gender == GENDER_NEUTER)
                          message = "thought it was a fish, and was mistaken";
                  else
                          message = "thought he was a fish and was mistaken";
                }
                else if(randomness < 0.6) {
                  if(gender == GENDER_FEMALE)
                          message = "didn't notice her absence of gills";
                  else if(gender == GENDER_NEUTER)
                          message = "didn't notice its absence of gills";
                  else
                          message = "didn't notice his absence of gills";
                } else if(randomness < 0.8) {
                 if(gender == GENDER_FEMALE)
					 message = "didn't realize she can't breathe underwater";
				 else if(gender == GENDER_NEUTER)
					 message = "didn't realize it can't breathe underwater";
				 else
					 message = "didn't realize he can't breathe underwater";
                }
                break;
              case MOD_SLIME:
                message = "melted";
                break;
              case MOD_LAVA:
                if(randomness < 0.3)
                  message = "does a back flip into the lava";
                else if(randomness < 0.35)
                  message = "shouldn't swim in the lava";
				else
					message = "got burnt";
                break;
              case MOD_TARGET_LASER:
                message = "saw the light";
                break;
              case MOD_TRIGGER_HURT:
                if(randomness < 0.1) {
					if(gender == GENDER_FEMALE)
							message = "lost her standing in the world";
					else if(gender == GENDER_NEUTER)
							message = "lost its standing in the world";
					else
							message = "lost his standing in the world";
                }
                else if(randomness < 0.15)
                        message = "was pwnd by the mapper";
                else if(randomness < 0.2)
                        message = "was caught trying to leave and shown the door";
                else if(randomness < 0.5)
                        message = "was killed by unknown";
                else
                        message = "was in the wrong place";
                break;
              case MOD_HSPAWN:
                if(randomness < 0.1)
                  message = "was pwned by a dying node";
                else if(randomness < 0.3)
                  message = "shouldn't kill that node";
                else
                  message = "should have run further";
                break;
              case MOD_ASPAWN:
                if(randomness < 0.15) {
                  if(gender == GENDER_FEMALE)
                          message = "got an egg...all over her";
                  else
                          message = "got an egg...all over him";
				}
				else
                  message = "shouldn't have trod in the acid";
                break;
              case MOD_MGTURRET:
                if(randomness < 0.2)
                        message = "was killed by a turret";
                else if(randomness < 0.5)
                        message = "was turned into swiss cheese";
                else
                        message = "was gunned down by a turret";
                break;
              case MOD_TESLAGEN:
                if(randomness < 0.2)
                  message = "was killed by a tesla generator";
                else if(randomness < 0.4)
                  message = "was electrocuted by a tesla generator";
                else if(randomness < 0.45)
                  message = "was fried by a tesla";
                else if(randomness < 0.5)
                  message = "would make a great battery";
                else
                  message = "was zapped by a tesla generator";
                  break;
              case MOD_ATUBE:
                if(randomness < 0.2)
                        message = "was killed by an acid tube";
                else if(randomness < 0.3)
                        message = "took an acid-tube shower";
                else if(randomness < 0.33)
                        message = "should eat more acid blobs";
                else
                        message = "was melted by an acid tube";
                break;
              case MOD_OVERMIND:
				if(randomness < 0.2)
				  message = "got too close to the overmind";
                else if(randomness < 0.3)
                  message = "shouldn't stay near the overmind";
                else if(randomness < 0.4)
                  message = "shouldn't hug the overmind";
                else if(randomness < 0.5)
                  message = "shouldn't touch the overmind";
                else if(randomness < 0.6)
                  message = "was masticated by the overmind";
                else if(randomness < 0.7)
                  message = "shook the overmind's hand";
				else 
					message = "got too close to the overmind";
                break;
              case MOD_REACTOR:
				if(randomness < 0.5)
					message = "got too close to the reactor";
                else if(randomness < 0.6)
                  message = "shouldn't stay near the reactor";
                else if(randomness < 0.7)
                  message = "should back away from the reactor";
                else
                  message = "got too close to the reactor";
                 break;
              case MOD_SLOWBLOB:
                if(randomness < 0.4)
                  message = "should visited a medistation";
                else if(randomness < 0.43)
                  message = "was killed by a trapper";
                else if(randomness < 0.5)
                  message = "was killed by a snot";
                else if(randomness < 0.55)
                  message = "was killed by a ball of shit";
                else
                  message = "should visited a medistation";
                 break;
               case MOD_RADIATION:
                if(randomness < 0.4)
                  message = "was irradiated by his base";
                else if(randomness < 0.6)
                  message = "shouldn't stay in base";
                else if(randomness < 0.8)
                  message = "killed by his own base";
                else if(randomness < 0.9)
                  message = "couldn't resist the RC radiation";
                else
                  message = "should make a tour";
                 break;
			   case MOD_POOP:
                if(randomness < 0.5)
                  message = "died in grangerbirth";
                else if(randomness < 0.6)
                  message = "died while pooping";
                else if(randomness < 0.8)
                  message = "shouldn't poop that granger";
                else if(randomness < 0.9)
                  message = "had a granger";
                else
                  message = "died in childbirth";
                 break;
              case MOD_SWARM:
                if(randomness < 0.3)
                  message = "was hunted down by the swarm";
                else if(randomness < 0.35)
                  message = "should have brought bug spray";
                else if(randomness < 0.4)
                  message = "should have kept running from the hive";
                else if(randomness < 0.7)
                  message = "died of hives";
                else
                  message = "was hunted down by the swarm";
                break;
              default:
                message = NULL;
                break;
            }

  if( attacker == self )
  {
    //gender = ci->gender;
    switch( mod )
    {
	  case MOD_SUICIDE:
				if(randomness < 0.2)
                  message = "suicides";
                else if(randomness < 0.4)
                  message = "wanted to end the pain";
                else if(randomness < 0.6)
                  message = "does hara-kiri(eeww!)";
                else if(randomness < 0.8)
                  if(gender == GENDER_FEMALE)
                          message = "killed herself";
                  else if(gender == GENDER_NEUTER)
                          message = "killed itself";
                  else
                          message = "killed himself";
                else {
                  if(gender == GENDER_FEMALE)
                          message = "lost her nerve";
                  else if(gender == GENDER_NEUTER)
                          message = "lost its nerve";
                  else
                          message = "lost his nerve";
                }
                break;
			   case MOD_POOP:
                if(randomness < 0.5)
                  message = "died in grangerbirth";
                else if(randomness < 0.6)
                  message = "died while pooping";
                else if(randomness < 0.8)
                  message = "shouldn't poop that granger";
                else if(randomness < 0.9)
                  message = "had a granger";
                else
                  message = "died in childbirth";
                 break;
    case MOD_FLAMER_SPLASH:
                if(randomness < 0.3) {
                        if( gender == GENDER_FEMALE )
                                message = "toasted herself";
                        else if( gender == GENDER_NEUTER )
                                message = "toasted itself";
                        else
                                message = "toasted himself";
                } else if(randomness < 0.6) {
                        if( gender == GENDER_FEMALE )
                                message = "pointed her flamethrower the wrong way";
                        else if( gender == GENDER_NEUTER )
                                message = "pointed its flamethrower the wrong way";
                        else
                                message = "pointed its flamethrower the wrong way";
                } else if(randomness < 0.8) {
                        if( gender == GENDER_FEMALE )
                                message = "set herself on fire";
                        else if( gender == GENDER_NEUTER )
                                message = "set itself on fire";
                        else
                                message = "set himself on fire";
                }
                else {
                        if( gender == GENDER_FEMALE )
                                message = "toasted herself";
                        else if( gender == GENDER_NEUTER )
                                message = "toasted itself";
                        else
                                message = "toasted himself";
                }
                break;

          case MOD_LCANNON_SPLASH:
            if(randomness < 0.5) {
                if( gender == GENDER_FEMALE )
                  message = "irradiated herself";
                else if( gender == GENDER_NEUTER )
                  message = "irradiated itself";
                else
                  message = "irradiated himself";
            }
            else {
                if( gender == GENDER_FEMALE)
                        message = "atomiced herself";
                else if(gender == GENDER_NEUTER)
                        message = "atomiced itself";
                else
                        message = "atomiced himself";
            }
            break;

          case MOD_GRENADE:
                  if(randomness < 0.3) {
                if( gender == GENDER_FEMALE )
                  message = "blew herself up";
                else if( gender == GENDER_NEUTER )
                  message = "blew itself up";
                else
                  message = "blew himself up";
				  }
                else if(randomness < 0.8 && randomness > 0.7) {
					if( gender == GENDER_FEMALE )
							message = "lost her grenade pin, anyone find it?";
					else if( gender == GENDER_NEUTER )
							message = "lost its grenade pin, anyone find it?";
					else
							message = "lost his grenade pin, anyone find it?";
				}
				else {
                if( gender == GENDER_FEMALE )
                  message = "blew herself up";
                else if( gender == GENDER_NEUTER )
                  message = "blew itself up";
                else
                  message = "blew himself up";
				  }
            break;

          default:
            if( gender == GENDER_FEMALE )
              message = "killed herself";
            else if( gender == GENDER_NEUTER )
              message = "killed itself";
            else
              message = "killed himself";
            break;
            }
  }

  if( message )
  {
    AP( va( "print \"%s %s.\n\"", targetName, message ));
    return;
  }
  // check for double client messages

  if( killer != ENTITYNUM_WORLD )
  {
    switch( mod )
    {
    case MOD_PAINSAW:
            if(randomness < 0.3)
                message = "was sawn by";
            else if(randomness < 0.8) {
                message = "got a good look of";
                message2 = "'s painsaw";
            }
			else
				message = "was sawn by";
            break;
          case MOD_BLASTER:
            if(randomness < 0.3)
                message = "was blasted by";
            else if(randomness < 0.6) {
                message = "was humiliated by";
                message2 = "'s Blaster";
            }
            else {
                message = "was blasted by";
            }
            break;
          case MOD_MACHINEGUN:
            message = "was machinegunned by";
            break;
          case MOD_CHAINGUN:
            if(randomness < 0.3)
                message = "was chaingunned by";
			else if(randomness < 0.6){
                message = "couldn't scape";
				message2 = "'s Chaingun";
			}
            else
                message = "was chaingunned by";            
            break;
          case MOD_SHOTGUN:
            if(randomness < 0.3)
                message = "was gunned down by";
            else if(randomness < 0.5) {
                message = "felt the force of";
                message2 = "'s Shotgun";
            }
            else if(randomness < 0.7) {
                message = "got shotgun'd by";
            } else {
                message = "was gunned down by";
            }
            break;
          case MOD_PRIFLE:
            if(randomness < 0.4)
                message = "was pulse rifled by";
            else if(randomness < 0.6)
                message = "was reduced to plasma by";
			else
                message = "was pulse rifled by";
            break;
          case MOD_MDRIVER:
            if(randomness < 0.2)
                message = "was mass driven by";
            else if(randomness < 0.25)
                message = "received a new orifice from";
            else if(randomness < 0.7) {
                message = "cannot escape";
                message2 = "'s Mass";
            } else {
                message = "was mass driven by";
            }
            break;
          case MOD_LASGUN:
            message = "was lasgunned by";
            break;
          case MOD_FLAMER:
            if(randomness < 0.3) {
                message = "was grilled by";
                message2 = "'s Flamer";
            } else if(randomness < 0.6) {
                message = "fried by";
				message2 = "'s Flammer";
            } else {
                message = "was grilled by";
                message2 = "'s Flamer";
            }
            break;
          case MOD_FLAMER_SPLASH:
            if(randomness < 0.4) {
                message = "was toasted by";
                message2 = "'s Flamer";
            }
            else if(randomness < 0.8) {
                message = "tried to warm up to";
                message2 = "'s Flamer";
            } else {
                message = "was toasted by";
                message2 = "'s Flamer";
            }
            break;
          case MOD_LCANNON:
            if(randomness < 0.3) {
                message = "felt the full force of";
                message2 = "'s Lucifer Cannon";
            } else if (randomness < 0.35) {
                message = "shouldn't play with";
                message2 = "'s glowing yellow balls";
            } else if (randomness < 0.4) {
                message = "was atomized by";
                message2 = "'s well-placed Luci spam";
            } else if (randomness < 0.5) {
                message = "received a charge from";
            } else {
                message = "felt the full force of";
                message2 = "'s Lucifer Cannon";
            }
            break;
          case MOD_LCANNON_SPLASH:
            if(randomness < 0.3) {
            message = "was caught in the fallout of";
            message2 = "'s Lucifer Cannon";
            } else if (randomness < 0.6) {
                message = "couldn't scape of";
                message2 = "'s Lucifer Cannon";
			}
	        message = "was caught in the fallout of";
            message2 = "'s Lucifer Cannon";
            break;
          case MOD_GRENADE:
            if(randomness < 0.4) {
                message = "couldn't escape";
                message2 = "'s Grenade";
            } else if(randomness < 0.7) {
                message = "got killed by";
                message2 = "'s Grenade";
            } else {
                message = "couldn't escape";
                message2 = "'s Grenade";
            }
            break;


          case MOD_ABUILDER_CLAW:
            if(randomness < 0.3) {
                message = "should leave";
                message2 = "'s buildings alone";
            } else if(randomness < 0.5) {
                message = "tried to pet";
                message2 = "'s gwanga and got bit";
            } else {
                message = "should leave";
                message2 = "'s buildings alone";
            }
            break;
          case MOD_LEVEL0_BITE:
            if(randomness < 0.1)
                message = "was bitten by";
            else if(randomness < 0.2) {
                message = "fell to the mastery of";
                message2 = "'s Dretch-Fu";
            }
            else if(randomness < 0.5) {
                message = "was dretch stormed by";
            }
            else if(randomness < 0.65 && randomness > 0.6) {
                message = "was outmanuevered by";
                message2 = "'s Dretch";
            }
            else {
                message = "was bitten by";
            }
            break;
          case MOD_LEVEL1_CLAW:
            message = "was swiped by";
            Com_sprintf( className, 64, "'s %s",
                BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL1 ) );
            message2 = className;
            break;
          case MOD_LEVEL2_CLAW:
            if(randomness < 0.5) {
                message = "was munched by";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL2 ) );
                    message2 = className;
            }
            else if(randomness < 0.9) {
                message = "was chomped by";
                message2 = "'s marauding Maruader";
            }
            else {
                message = "was clawed by";
				message2 = "'s Marauder";
            }
            break;
          case MOD_LEVEL2_ZAP:
            message = "was zapped by";
            Com_sprintf( className, 64, "'s %s",
                BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL2 ) );
            message2 = className;
            break;
          case MOD_LEVEL3_CLAW:
            message = "was chomped by";
            Com_sprintf( className, 64, "'s %s",
                BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL3 ) );
            message2 = className;
            break;
          case MOD_LEVEL3_POUNCE:
            if(randomness < 0.4) {
                message = "was pounced upon by";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL3 ) );
                message2 = className;
            }
            else if(randomness < 0.5) {
                message = "gave";
                message2 = "'s goon a hug";
            }
            else {
                message = "was pounced upon by";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL3 ) );
                message2 = className;
            }
            break;
          case MOD_LEVEL3_BOUNCEBALL:
            if(randomness < 0.3) {
                message = "was sniped by";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL3 ) );
                message2 = className;
            } else if(randomness < 0.5) {
                message = "tried to catch";
                message2 = "'s barb";
			} else {
                message = "was sniped by";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL3 ) );
                message2 = className;
			}
            break;
          case MOD_LEVEL4_CLAW:
            if(randomness < 0.3) {
                message = "was mauled by";
                Com_sprintf( className, 64, "'s %s",
                                BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
                message2 = className;
            } else if(randomness < 0.6) {
                message = "got mutilated by";
                Com_sprintf( className, 64, "'s %s",
                                BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
                message2 = className;
            } else {
                message = "was mauled by";
                Com_sprintf( className, 64, "'s %s",
                                BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
                message2 = className;
            }
            break;
          case MOD_LEVEL4_CHARGE:
                  if(randomness < 0.3) {
                message = "should have gotten out of the way of";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
                message2 = className;
                  }
                  else if(randomness < 0.4) {
                          message = "was between";
                          message2 = "'s rant and a hard place";
                  }
                  else if(randomness < 0.6) {
                          message = "froze in";
                          message2 = "headlights";
                  }
                  else if(randomness < 0.75) {
                          message = "just wanted to be friends with";
                          message2 = "'s tyrant";
                  } else {
                message = "should have gotten out of the way of";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
                message2 = className;
                  }
            break;

          case MOD_LEVEL4_CRUSH:
                  if(randomness < 0.3) {
                message = "was squished by";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
                message2 = className;
                  }
                  else if(randomness < 0.4) {
                          message = "was between";
						  message2 = "'s Tyrant and the ground";
                  }
                  else if(randomness < 0.6) {
                          message = "was turned into Pate by";
                          message2 = "'s ass";
                  } else {
                message = "was squished by";
                Com_sprintf( className, 64, "'s %s",
                    BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
                message2 = className;
                  }
            break;

          case MOD_POISON:
            message = "should have used a medkit against";
            message2 = "'s poison";
            break;
          case MOD_LEVEL1_PCLOUD:
            if(randomness < 0.5) {
                message = "was gassed by";
                Com_sprintf( className, 64, "'s %s",
                                BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL1 ) );
                message2 = className;
            } else if(randomness < 0.9) {
                message = "smelled";
                message2 = "'s cologne";
            } else {
                message = "was gassed by";
                Com_sprintf( className, 64, "'s %s",
                                BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL1 ) );
                message2 = className;
            }
            break;


          case MOD_TELEFRAG:
            message = "tried to invade";
            message2 = "'s personal space";
            break;
          default:
            message = "was killed by";
			message2 = "'s mass";
            break;

    }

	if( level.instaGibMode ) tk = qfalse;

    if( message && level.instaGibMode && mod == MOD_MDRIVER && level.numLiveHumanClients < 3 && g_extremeSuddenDeath.integer ) {
	  AP( va( "print \"%s was mass driven by %s\n%s ^3WINS!\n\"",
        targetName,
        attackerName,
		attackerName ) );
	  AP( va( "cp \"^7%s ^3WINS!", attackerName ) );
      return;
	} else if( message && level.instaGibMode && mod == MOD_MDRIVER && g_instaGibMode.integer == 3 ) {
      AP( va( "print \"%s was mass driven by %s^7, had %i lives left\n\"",
        targetName,
        attackerName,
		g_instaGibLives.integer - self->client->pers.statscounters.deaths - 1) );
      return;
	} else if( message && level.instaGibMode && mod == MOD_MDRIVER ) {
      AP( va( "print \"%s was mass driven by %s\n\"",
        targetName,
        attackerName ) );
      return;
	}
	else if( message && !tk) {
      AP( va( "print \"%s %s %s%s\n\"",
        targetName, message,
//        ( tk ) ? S_COLOR_RED "TEAMMATE " S_COLOR_WHITE : "",
        attackerName, message2 ));
	 return;
	} else if( message && tk)
		trap_SendServerCommand( -1,
      va( "print \"%s^7 was killed by ^1TEAMMATE^7 %s^7 (Did %d damage to %d max)\n\"",
      self->client->pers.netname, attacker->client->pers.netname, self->client->tkcredits[ attacker->s.number ], self->client->ps.stats[ STAT_MAX_HEALTH ] ) );
	return;
  }

  // we don't know what it was
  AP( va( "print \"%s died.\n\"", targetName ));
}


/*
==================
player_die
==================
*/
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
{
  //gentity_t *ent;
  int       anim;
  int       killer;
  int       i, j;
  char      *killerName, *obit;
  float     totalTK = 0;
  float     totalDamage = 0.0f;
  float     percentDamage = 0.0f;
  gentity_t *player;
  qboolean  tk = qfalse;
  int       spreeRate = 0;


  if( self->client->ps.pm_type == PM_DEAD )
    return;
  
  if(attacker->client && attacker != self && attacker->client->ps.stats[ STAT_PTEAM ]  == self->client->ps.stats[ STAT_PTEAM ] ) 
  {
    attacker->client->pers.statscounters.teamkills++;
    if( attacker->client->pers.teamSelection == PTE_ALIENS ) 
    {
      level.alienStatsCounters.teamkills++;
    }
    else if( attacker->client->pers.teamSelection == PTE_HUMANS )
    {
     level.humanStatsCounters.teamkills++;
    }
  }

  if( level.intermissiontime )
    return;

  // stop any following clients
  // r1: removed, annoying.

  self->client->ps.pm_type = PM_DEAD;
  self->suicideTime = 0;

  if( attacker )
  {
    killer = attacker->s.number;

    if( attacker->client )
    {
      killerName = attacker->client->pers.netname;
      tk = ( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ] 
        == self->client->ps.stats[ STAT_PTEAM ] );
    }
    else
      killerName = "<non-client>";
  }
  else
  {
    killer = ENTITYNUM_WORLD;
    killerName = "<world>";
  }

  if( killer < 0 || killer >= MAX_CLIENTS )
  {
    killer = ENTITYNUM_WORLD;
    killerName = "<world>";
  }

  if( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) )
    obit = "<bad obituary>";
  else
    obit = modNames[ meansOfDeath ];

  G_LogPrintf("Kill: %i %i %i: %s^7 killed %s^7 by %s\n",
    killer, self->s.number, meansOfDeath, killerName,
    self->client->pers.netname, obit );

  //TA: close any menus the client has open
  G_CloseMenus( self->client->ps.clientNum );

  //TA: deactivate all upgrades
  for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
    BG_DeactivateUpgrade( i, self->client->ps.stats );

  // killing spree over
  if( self->client->pers.statscounters.spreekills == -1 )
  {
    spreeRate = 2;
    trap_SendServerCommand( -1,
     va( "print \"%s^7's killing spree has come to an end\n\"",
     self->client->pers.netname ) );
  }
  self->client->pers.statscounters.spreekills = 0;
    
  if( meansOfDeath == MOD_SLAP )
  {
    if( self == attacker || !attacker )
    {
     if( g_slapKnockback.integer ){
      trap_SendServerCommand( -1, va( "print \"%s^7 slapped themself silly\n\"",
        self->client->pers.netname ) );
     }
     else {
      trap_SendServerCommand( -1, va( "print \"%s^7 suffered a heart attack\n\"",
        self->client->pers.netname ));
     }
    }
    else
      trap_SendServerCommand( -1,
        va( "print \"%s^7 felt %s^7's authoritay\n\"",
        self->client->pers.netname, killerName ) );

    // do not send obituary or credit any kills by slapping, skip straight to
    // setting the animations etc
    goto finish_dying;
  }
  
  else if( meansOfDeath == MOD_EXPLODE )
    {
      if( self == attacker || !attacker )
        trap_SendServerCommand( -1, va( "print \"%s^7 blew up in a firey death.\n\"",
          self->client->pers.netname ) );
      else
        trap_SendServerCommand( -1,
          va( "print \"^7%s^7 shouldn't have stood so close to ^7%s^7\n\"",
          self->client->pers.netname, killerName ) );

      // do not send obituary or credit any kills by slapping, skip straight to
      // setting the animations etc
      goto finish_dying;
    }


  // broadcast the death event to everyone
  else if( !tk )
  {
	  Print_Obituary( self, attacker, meansOfDeath, tk);


  /*// this works as a temporary hack until the client supports it
  if ( meansOfDeath == MOD_RADIATION )
    trap_SendServerCommand( -1, va( "print \"%s^7 was irradiated by his base\n\"", self->client->pers.netname ) );
  else if ( meansOfDeath == MOD_LEVEL2_CLAW && g_maraObituary.integer )
    trap_SendServerCommand( -1, va( "print \"%s^7 bit off %s^7's face\n\"", attacker->client->pers.netname, self->client->pers.netname ) );
  else if (meansOfDeath == MOD_SUICIDE )
	  trap_SendServerCommand ( -1, va ( "print \"%s^7 killed himself(idiot)\n\"", self->client->pers.netname ) ); 
  else 
  {
    ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
    ent->s.eventParm = meansOfDeath;
    ent->s.otherEntityNum = self->s.number;
    ent->s.otherEntityNum2 = killer;
    ent->r.svFlags = SVF_BROADCAST; // send to everyone
  }*/
  
  }
  else if (attacker->client)
  {
    // tjw: obviously this is a hack and belongs in the client, but
    //      this works as a temporary fix.
	  Print_Obituary( self, attacker, meansOfDeath, tk);
    /*trap_SendServerCommand( -1,
      va( "print \"%s^7 was killed by ^1TEAMMATE^7 %s^7 (Did %d damage to %d max)\n\"",
      self->client->pers.netname, attacker->client->pers.netname, self->client->tkcredits[ attacker->s.number ], self->client->ps.stats[ STAT_MAX_HEALTH ] ) );*/
    if( !level.instaGibMode )
	  trap_SendServerCommand( attacker - g_entities,
		va( "cp \"You killed ^1TEAMMATE^7 %s\"", self->client->pers.netname ) );
	else {
	  trap_SendServerCommand( attacker - g_entities,
		va( "cp \"You fragged ^7%s\"", self->client->pers.netname ) );
	}

    G_admin_tklog_log( attacker, self, meansOfDeath );
  }

  self->enemy = attacker;

  self->client->ps.persistant[ PERS_KILLED ]++;
  self->client->pers.statscounters.deaths++;
  if( self->client->pers.teamSelection == PTE_ALIENS ) 
  {
   level.alienStatsCounters.deaths++;
  }
  else if( self->client->pers.teamSelection == PTE_HUMANS )
  {
    level.humanStatsCounters.deaths++;
  }

  if( attacker && attacker->client )
  {
    attacker->client->lastkilled_client = self->s.number;
	  
   if( g_KillerHP.integer ) 
   {
     trap_SendServerCommand( self-g_entities, va( "print \"Your killer, %s^7, had %3i HP.\n\"", killerName, attacker->health ) );
   }

    if( attacker == self || OnSameTeam( self, attacker ) && !level.instaGibMode )
    {
      AddScore( attacker, -1 );

      // Normal teamkill penalty
      if( !g_retribution.integer ) {
        if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
          G_AddCreditToClient( attacker->client, -FREEKILL_ALIEN, qtrue );
        else if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
          G_AddCreditToClient( attacker->client, -FREEKILL_HUMAN, qtrue );
      }
    }
    else
    {
      AddScore( attacker, 1 );

		if( level.instaGibMode &&  attacker->client->ps.weapon == WP_MASS_DRIVER ) {
		  int ammo, maxAmmo;

		  BG_FindAmmoForWeapon( WP_MASS_DRIVER, &maxAmmo, NULL );
		  BG_UnpackAmmoArray( WP_MASS_DRIVER,  attacker->client->ps.ammo,  attacker->client->ps.powerups, &ammo, NULL );
		  ammo = ammo + 3;
		  BG_PackAmmoArray( WP_MASS_DRIVER,  attacker->client->ps.ammo,  attacker->client->ps.powerups, ammo, 0 );
		}


      attacker->client->lastKillTime = level.time;
      attacker->client->pers.statscounters.kills++;
      if( attacker->client->pers.teamSelection == PTE_ALIENS ) 
      {
       level.alienStatsCounters.kills++;
      }
      else if( attacker->client->pers.teamSelection == PTE_HUMANS )
      {
        //ROTAX
    	  if (g_ambush.integer == 1 && ROTACAK_ambush_rebuild_time_temp < level.time)//V rebuild time nepocitam killy
    	  {
          if( !tk )
          {
            ROTACAK_ambush_kills++;
            
            //ted to nastavi stage 2 atd.
            if (ROTACAK_ambush_kills == g_ambush_granger_s1.integer
             || (ROTACAK_ambush_kills - g_ambush_granger_s1.integer) == g_ambush_dretch_s2.integer
             || (ROTACAK_ambush_kills - g_ambush_granger_s1.integer - g_ambush_dretch_s2.integer) == g_ambush_basilisk_s3.integer
             || (ROTACAK_ambush_kills - g_ambush_granger_s1.integer - g_ambush_dretch_s2.integer - g_ambush_basilisk_s3.integer) == g_ambush_basilisk2_s4.integer
             || (ROTACAK_ambush_kills - g_ambush_granger_s1.integer - g_ambush_dretch_s2.integer - g_ambush_basilisk_s3.integer - g_ambush_basilisk2_s4.integer) == g_ambush_marauder_s5.integer
             || (ROTACAK_ambush_kills - g_ambush_granger_s1.integer - g_ambush_dretch_s2.integer - g_ambush_basilisk_s3.integer - g_ambush_basilisk2_s4.integer - g_ambush_marauder_s5.integer) == g_ambush_marauder2_s6.integer
             || (ROTACAK_ambush_kills - g_ambush_granger_s1.integer - g_ambush_dretch_s2.integer - g_ambush_basilisk_s3.integer - g_ambush_basilisk2_s4.integer - g_ambush_marauder_s5.integer - g_ambush_marauder2_s6.integer) == g_ambush_dragon_s7.integer
             || (ROTACAK_ambush_kills - g_ambush_granger_s1.integer - g_ambush_dretch_s2.integer - g_ambush_basilisk_s3.integer - g_ambush_basilisk2_s4.integer - g_ambush_marauder_s5.integer - g_ambush_marauder2_s6.integer - g_ambush_dragon_s7.integer) == g_ambush_dragon2_s8.integer)
              ambush_next_stage();
            else if ((ROTACAK_ambush_kills - g_ambush_granger_s1.integer - g_ambush_dretch_s2.integer - g_ambush_basilisk_s3.integer - g_ambush_basilisk2_s4.integer - g_ambush_marauder_s5.integer - g_ambush_marauder2_s6.integer - g_ambush_dragon_s7.integer - g_ambush_dragon2_s8.integer) == g_ambush_tyrants_to_win.integer)
            {
              //misto humans wins se alienum znici spawny
              int       i;
              gentity_t *e;
          
              for( i = 1, e = g_entities + i; i < level.num_entities; i++, e++ )
              {
                if( e->s.modelindex == BA_A_SPAWN )
                  G_Damage( e, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
              }
            
              trap_SendServerCommand( -1, va( "cp \"^1 ====> WELL DONE - NO EGGS <====  ^7\"" ) );
              trap_SendServerCommand( -1, va( "print \"^1 ====> WELL DONE - NO EGGS <====  ^7\n\"" ) );
              
              //turn ff off, otherwise all tyrants kill each others without humans
              if (g_ambush_no_egg_ffoff.integer == 1)
              {
                trap_Cvar_Set( "g_friendlyFire", 0 );
                trap_Cvar_Set( "g_friendlyFireAliens", 0 );
              }
            }
          }
        }
        
        level.humanStatsCounters.kills++;
      }

      if( g_killingSpree.integer > 2 )
      {
        if( attacker->client->pers.statscounters.spreekills >= 0 )
          attacker->client->pers.statscounters.spreekills += 60;
        if( attacker->client->pers.statscounters.spreekills > ( g_killingSpree.integer - 1 ) * 60 )
        {
          attacker->client->pers.statscounters.spreekills = -1;
          trap_SendServerCommand( -1,
            va( "print \"%s^3 is on a killing spree! killer gets a double reward bonus\n\"",
            attacker->client->pers.netname ) );
        }
      }
     }
    
    if( attacker == self )
    {
      attacker->client->pers.statscounters.suicides++;
      if( attacker->client->pers.teamSelection == PTE_ALIENS ) 
      {
       level.alienStatsCounters.suicides++;
      }
      else if( attacker->client->pers.teamSelection == PTE_HUMANS )
      {
        level.humanStatsCounters.suicides++;
      }
    }
  }
  else if( attacker->s.eType != ET_BUILDABLE )
    AddScore( self, -1 );

  //total up all the damage done by every client
  for( i = 0; i < MAX_CLIENTS; i++ )
  {
    totalDamage += (float)self->credits[ i ];
    totalTK += (float)self->client->tkcredits[ i ];
  }
  // punish players for damaging teammates
  if ( g_retribution.integer && totalTK && !level.instaGibMode )
  {
    int totalPrice;
    int max = HUMAN_MAX_CREDITS;

    if ( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
    {
      totalPrice = BG_ClassCanEvolveFromTo( PCL_ALIEN_LEVEL0, self->client->ps.stats[ STAT_PCLASS ], ALIEN_MAX_KILLS, 0 );
      max = ALIEN_MAX_KILLS;
    }
    else
    {
      totalPrice = BG_GetValueOfEquipment( &self->client->ps );
    }

    if ( self->client->ps.persistant[ PERS_CREDIT ] + totalPrice > max )
      totalPrice = max - self->client->ps.persistant[ PERS_CREDIT ];

    if ( totalPrice > 0 )
    {
      totalTK += totalDamage;
      if( totalTK < self->client->ps.stats[ STAT_MAX_HEALTH ] )
        totalTK = self->client->ps.stats[ STAT_MAX_HEALTH ];

      if ( self->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
      {
        for ( i = 0; i < MAX_CLIENTS; i++ )
        {
          int price;
          // no retribution if self damage or enemmy damage or building damage or no damage from this client
          if ( i == self - g_entities || !g_entities[ i ].client || !OnSameTeam( &g_entities[ i ], self ) || !self->client->tkcredits[ i ] )
            continue;

          // calculate retribution price (rounded up)
          price = ( totalPrice * self->client->tkcredits[ i ] ) / totalTK + 0.5f;
          self->client->tkcredits[ i ] = 0;

          // check for enough credits
          if ( g_entities[ i ].client->ps.persistant[ PERS_CREDIT ] < price )
            price = g_entities[ i ].client->ps.persistant[ PERS_CREDIT ];
          if ( price )
          {
            G_AddCreditToClient( self->client, price, qtrue );
            G_AddCreditToClient( g_entities[ i ].client, -price, qtrue );

            trap_SendServerCommand( self->client->ps.clientNum, va( "print \"Received ^3%d credits ^7from %s ^7in retribution.\n\"", price, g_entities[ i ].client->pers.netname ) );
            trap_SendServerCommand( g_entities[ i ].client->ps.clientNum, va( "print \"Transfered ^3%d credits ^7to %s ^7in retribution.\n\"", price, self->client->pers.netname ) );
          }
        }
      }
      else
      {
        int toPay[ MAX_CLIENTS ] = { 0 };
        int frags = totalPrice;
        int damageForEvo = totalTK / totalPrice;
        for ( i = 0; i < MAX_CLIENTS; i++ )
        {
          // no retribution if self damage or enemmy damage or building damage or no damage from this client
          if ( i == self - g_entities || !g_entities[ i ].client || !OnSameTeam( &g_entities[ i ], self ) || !self->client->tkcredits[ i ] )
            continue;

          // find out how many full evos this client needs to pay
          toPay[ i ] = ( totalPrice * self->client->tkcredits[ i ] ) / totalTK;
          if ( toPay[ i ] > g_entities[ i ].client->ps.persistant[ PERS_CREDIT ] )
            toPay[ i ] = g_entities[ i ].client->ps.persistant[ PERS_CREDIT ];
          frags -= toPay[ i ];
          self->client->tkcredits[ i ] -= damageForEvo * toPay[ i ];
        }

        // if we have not met the evo count, continue stealing evos
        while ( 1 )
        {
          int maximum = 0;
          int topClient = 0;
          for ( i = 0; i < MAX_CLIENTS; i++ )
          {
            if ( self->client->tkcredits[ i ] > maximum && g_entities[ i ].client->ps.persistant[ PERS_CREDIT ] )
            {
              maximum = self->client->tkcredits[ i ];
              topClient = i;
            }
          }
          if ( !maximum )
            break;
          toPay[ topClient ]++;
          self->client->tkcredits[ topClient ] = 0;
          frags--;
          if ( !frags )
           break;
        }
        
        // now move the evos around
        for ( i = 0; i < MAX_CLIENTS; i++ )
        {
          if ( !toPay[ i ] )
            continue;
          
          G_AddCreditToClient( self->client, toPay[ i ], qtrue );
          G_AddCreditToClient( g_entities[ i ].client, -toPay[ i ], qtrue );

          trap_SendServerCommand( self->client->ps.clientNum, va( "print \"Received ^3%d ^7evos from %s ^7in retribution.\n\"", toPay[ i ], g_entities[ i ].client->pers.netname ) );
          trap_SendServerCommand( g_entities[ i ].client->ps.clientNum, va( "print \"Transfered ^3%d ^7evos to %s ^7in retribution.\n\"", toPay[ i ], self->client->pers.netname ) );
        }
      }
    }
  }

  // if players did more than DAMAGE_FRACTION_FOR_KILL increment the stage counters
  if( !OnSameTeam( self, attacker ) && totalDamage >= ( self->client->ps.stats[ STAT_MAX_HEALTH ] * DAMAGE_FRACTION_FOR_KILL ) )
  {
    if( self->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) 
    {
      trap_Cvar_Set( "g_alienKills", va( "%d", g_alienKills.integer + 1 ) );
      if( g_alienStage.integer < 2 )
      {
		self->client->pers.statscounters.feeds++;
		level.humanStatsCounters.feeds++;

        if( g_feedingSpree.integer &&
            level.reactorPresent &&
            !G_BuildableRange( self->client->ps.origin, 600, BA_H_REACTOR ) &&
            !G_BuildableRange( self->client->ps.origin, 200, BA_H_SPAWN ) )
        {
          self->client->pers.statscounters.spreefeeds += SPREE_FEED_VALUE;
        }
      }
    }
    else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
    {
      trap_Cvar_Set( "g_humanKills", va( "%d", g_humanKills.integer + 1 ) );
      if( g_humanStage.integer < 2 )
      {
	self->client->pers.statscounters.feeds++;
	level.alienStatsCounters.feeds++;

        if( g_feedingSpree.integer &&
            level.overmindPresent &&
            !G_BuildableRange( self->client->ps.origin, 600, BA_A_OVERMIND ) &&
            !G_BuildableRange( self->client->ps.origin, 200, BA_A_SPAWN ) )
        {
          self->client->pers.statscounters.spreefeeds += SPREE_FEED_VALUE;
        }
      }
    }
  }

  if( totalDamage > 0.0f )
  {
    if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
    {
      //nice simple happy bouncy human land
      float classValue = BG_FindValueOfClass( self->client->ps.stats[ STAT_PCLASS ] );

      for( i = 0; i < MAX_CLIENTS; i++ )
      {
        player = g_entities + i;

        if( !player->client )
          continue;

        if( player->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
          continue;

        if( !self->credits[ i ] )
          continue;
	
	percentDamage = (float)self->credits[ i ] / totalDamage;
	if( percentDamage > 0 && percentDamage < 1)
	{
	  player->client->pers.statscounters.assists++;
          level.humanStatsCounters.assists++;

	}

        if( spreeRate && player == attacker )
          percentDamage *= (float)spreeRate;

        //add credit
        G_AddCreditToClient( player->client,
            (int)( classValue * percentDamage ), qtrue );
      }
    }
    else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
    {
      //horribly complex nasty alien land
      float humanValue = BG_GetValueOfHuman( &self->client->ps );
      int   frags;
      int   unclaimedFrags = (int)humanValue;

      for( i = 0; i < MAX_CLIENTS; i++ )
      {
        player = g_entities + i;

        if( !player->client )
          continue;

        if( player->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
          continue;

        //this client did no damage
        if( !self->credits[ i ] )
          continue;

        //nothing left to claim
        if( !unclaimedFrags )
          break;

	percentDamage = (float)self->credits[ i ] / totalDamage;
	if( percentDamage > 0 && percentDamage < 1)
	{
	  player->client->pers.statscounters.assists++;
	  level.alienStatsCounters.assists++;
	}
	
        frags = (int)floor( humanValue * percentDamage);

        if( frags > 0 )
        {
          //add kills
          if( spreeRate && player == attacker )
            G_AddCreditToClient( player->client, frags * spreeRate, qtrue );
          else
            G_AddCreditToClient( player->client, frags, qtrue );

          //can't revist this account later
          self->credits[ i ] = 0;

          //reduce frags left to be claimed
          unclaimedFrags -= frags;
        }
      }

      //there are frags still to be claimed
      if( unclaimedFrags )
      {
        //the clients remaining at this point do not
        //have enough credit to claim even one frag
        //so simply give the top <unclaimedFrags> clients
        //a frag each

        for( i = 0; i < unclaimedFrags; i++ )
        {
          int maximum = 0;
          int topClient = 0;

          for( j = 0; j < MAX_CLIENTS; j++ )
          {
            //this client did no damage
            if( !self->credits[ j ] )
              continue;

            if( self->credits[ j ] > maximum )
            {
              maximum = self->credits[ j ];
              topClient = j;
            }
          }

          if( maximum > 0 )
          {
            player = g_entities + topClient;

            //add kills
            if( spreeRate && player == attacker )
              G_AddCreditToClient( player->client, spreeRate, qtrue );
            else
              G_AddCreditToClient( player->client, 1, qtrue );

            //can't revist this account again
            self->credits[ topClient ] = 0;
          }
        }
      }
    }
  }

  ScoreboardMessage( self );    // show scores

  // send updated scores to any clients that are following this one,
  // or they would get stale scoreboards
  for( i = 0 ; i < level.maxclients ; i++ )
  {
    gclient_t *client;

    client = &level.clients[ i ];
    if( client->pers.connected != CON_CONNECTED )
      continue;

    if( client->sess.sessionTeam != TEAM_SPECTATOR )
      continue;

    if( client->sess.spectatorClient == self->s.number )
      ScoreboardMessage( g_entities + i );
  }
finish_dying:

  self->client->pers.classSelection = PCL_NONE; //TA: reset the classtype
  VectorCopy( self->s.origin, self->client->pers.lastDeathLocation );

  self->takedamage = qfalse; // can still be gibbed

  self->s.weapon = WP_NONE;
  self->r.contents = CONTENTS_CORPSE;

  self->s.angles[ PITCH ] = 0;
  self->s.angles[ ROLL ] = 0;
  self->s.angles[ YAW ] = self->s.apos.trBase[ YAW ];
  LookAtKiller( self, inflictor, attacker );

  VectorCopy( self->s.angles, self->client->ps.viewangles );

  self->s.loopSound = 0;

  self->r.maxs[ 2 ] = -8;

  // don't allow respawn until the death anim is done
  // g_forcerespawn may force spawning at some later time
  self->client->respawnTime = level.time + 1700;

  if( g_feedingSpree.integer )
  {
    int maxfeed;

    maxfeed = g_feedingSpree.integer * SPREE_FEED_VALUE;
    if( self->client->pers.statscounters.spreefeeds > maxfeed )
    {
      self->client->respawnTime += SPREE_FEED_DELAY * (self->client->pers.statscounters.spreefeeds - maxfeed );
      trap_SendServerCommand( self->client->ps.clientNum,
        va( "cp \"You are on a feeding spree! respawn delayed %d seconds\n\"",
        (self->client->respawnTime - level.time) / 1000 ) );
    }
  }

  if( self->client->pers.bleeder )
  {
    int spreeLength;

    spreeLength = self->client->pers.statscounters.spreebleeds / 10;
    self->client->respawnTime += 9 * 1000;
    
    trap_SendServerCommand( self->client->ps.clientNum,
      va( "print \"^3Bleeding has made you an enemy of your own base for ^7%d:%02d\n^1Respawn delayed ^7%d^1 seconds\n",
        spreeLength / 60, spreeLength % 60,
        ( self->client->respawnTime - level.time) / 1000 ) );
    trap_SendServerCommand( self->client->ps.clientNum,
      va( "cp \"^3Bleeding made you an enemy of your own base!\n\n^7for %d:%02d\n\n^1Respawn delayed %d seconds\"",
        spreeLength / 60, spreeLength % 60,
        ( self->client->respawnTime - level.time) / 1000 ) );
  }

  // remove powerups
  memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) );

  {
    // normal death
    static int i;

    if( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) )
    {
      switch( i )
      {
        case 0:
          anim = BOTH_DEATH1;
          break;
        case 1:
          anim = BOTH_DEATH2;
          break;
        case 2:
        default:
          anim = BOTH_DEATH3;
          break;
      }
    }
    else
    {
      switch( i )
      {
        case 0:
          anim = NSPA_DEATH1;
          break;
        case 1:
          anim = NSPA_DEATH2;
          break;
        case 2:
        default:
          anim = NSPA_DEATH3;
          break;
      }
    }

    self->client->ps.legsAnim =
      ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;

    if( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) )
    {
      self->client->ps.torsoAnim =
        ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
    }

    // use own entityid if killed by non-client to prevent uint8_t overflow
    G_AddEvent( self, EV_DEATH1 + i,
      ( killer < MAX_CLIENTS ) ? killer : self - g_entities );

    // globally cycle through the different death animations
    i = ( i + 1 ) % 3;
  }

	if( g_extremeSuddenDeath.integer || g_rampage.integer ) 
	{
		if( self->client->pers.teamSelection == PTE_ALIENS ) 
		{
		AP( va( "cp \"^1%i alien(s) remaining!", level.numLiveAlienClients - 1 ) );
		}
		else if( self->client->pers.teamSelection == PTE_HUMANS ) 
		{
			AP( va( "cp \"^4%i human(s) remaining!", level.numLiveHumanClients - 1 ) );
		}
		return;
	}

	//if( g_extremeSuddenDeath.integer ) 
	//{
	//    if( self->client->pers.teamSelection == PTE_HUMANS && level.numLiveHumanClients > 2) 
	//	{
	//		AP( va( "cp \"^4%i human(s) remaining!", level.numLiveHumanClients - 1 ) );
	//	} else if( self->client->pers.teamSelection == PTE_HUMANS && level.numLiveHumanClients < 3 ) {
	//		int i;
	//		gclient_t *winner;
	//		gclient_t *p;

	//		for( i = 0; i < level.maxclients; i++ )
	//		  {
	//			p = &level.clients[ i ];
	//			if( p->ps.stats[ STAT_HEALTH ] <= 0 ||
 //                 p->sess.sessionTeam == TEAM_SPECTATOR )
	//			  break;
	//			else winner = p;
	//		}
	//		AP( va( "cp \"^7%s ^3wins!", winner->pers.netname ) );
	//	}
	//	return;
	//}
  trap_LinkEntity( self );
}


////////TA: locdamage

/*
===============
G_ParseArmourScript
===============
*/
void G_ParseArmourScript( char *buf, int upgrade )
{
  char  *token;
  int   count;

  count = 0;

  while( 1 )
  {
    token = COM_Parse( &buf );

    if( !token[0] )
      break;

    if( strcmp( token, "{" ) )
    {
      G_Printf( "Missing { in armour file\n" );
      break;
    }

    if( count == MAX_ARMOUR_REGIONS )
    {
      G_Printf( "Max armour regions exceeded in locdamage file\n" );
      break;
    }

    //default
    g_armourRegions[ upgrade ][ count ].minHeight = 0.0;
    g_armourRegions[ upgrade ][ count ].maxHeight = 1.0;
    g_armourRegions[ upgrade ][ count ].minAngle = 0;
    g_armourRegions[ upgrade ][ count ].maxAngle = 360;
    g_armourRegions[ upgrade ][ count ].modifier = 1.0;
    g_armourRegions[ upgrade ][ count ].crouch = qfalse;

    while( 1 )
    {
      token = COM_ParseExt( &buf, qtrue );

      if( !token[0] )
      {
        G_Printf( "Unexpected end of armour file\n" );
        break;
      }

      if( !Q_stricmp( token, "}" ) )
      {
        break;
      }
      else if( !strcmp( token, "minHeight" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "0" );

        g_armourRegions[ upgrade ][ count ].minHeight = atof( token );
      }
      else if( !strcmp( token, "maxHeight" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "100" );

        g_armourRegions[ upgrade ][ count ].maxHeight = atof( token );
      }
      else if( !strcmp( token, "minAngle" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "0" );

        g_armourRegions[ upgrade ][ count ].minAngle = atoi( token );
      }
      else if( !strcmp( token, "maxAngle" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "360" );

        g_armourRegions[ upgrade ][ count ].maxAngle = atoi( token );
      }
      else if( !strcmp( token, "modifier" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "1.0" );

        g_armourRegions[ upgrade ][ count ].modifier = atof( token );
      }
      else if( !strcmp( token, "crouch" ) )
      {
        g_armourRegions[ upgrade ][ count ].crouch = qtrue;
      }
    }

    g_numArmourRegions[ upgrade ]++;
    count++;
  }
}


/*
===============
G_ParseDmgScript
===============
*/
void G_ParseDmgScript( char *buf, int class )
{
  char  *token;
  int   count;

  count = 0;

  while( 1 )
  {
    token = COM_Parse( &buf );

    if( !token[0] )
      break;

    if( strcmp( token, "{" ) )
    {
      G_Printf( "Missing { in locdamage file\n" );
      break;
    }

    if( count == MAX_LOCDAMAGE_REGIONS )
    {
      G_Printf( "Max damage regions exceeded in locdamage file\n" );
      break;
    }

    //default
    g_damageRegions[ class ][ count ].minHeight = 0.0;
    g_damageRegions[ class ][ count ].maxHeight = 1.0;
    g_damageRegions[ class ][ count ].minAngle = 0;
    g_damageRegions[ class ][ count ].maxAngle = 360;
    g_damageRegions[ class ][ count ].modifier = 1.0;
    g_damageRegions[ class ][ count ].crouch = qfalse;

    while( 1 )
    {
      token = COM_ParseExt( &buf, qtrue );

      if( !token[0] )
      {
        G_Printf( "Unexpected end of locdamage file\n" );
        break;
      }

      if( !Q_stricmp( token, "}" ) )
      {
        break;
      }
      else if( !strcmp( token, "minHeight" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "0" );

        g_damageRegions[ class ][ count ].minHeight = atof( token );
      }
      else if( !strcmp( token, "maxHeight" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "100" );

        g_damageRegions[ class ][ count ].maxHeight = atof( token );
      }
      else if( !strcmp( token, "minAngle" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "0" );

        g_damageRegions[ class ][ count ].minAngle = atoi( token );
      }
      else if( !strcmp( token, "maxAngle" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "360" );

        g_damageRegions[ class ][ count ].maxAngle = atoi( token );
      }
      else if( !strcmp( token, "modifier" ) )
      {
        token = COM_ParseExt( &buf, qfalse );

        if ( !token[0] )
          strcpy( token, "1.0" );

        g_damageRegions[ class ][ count ].modifier = atof( token );
      }
      else if( !strcmp( token, "crouch" ) )
      {
        g_damageRegions[ class ][ count ].crouch = qtrue;
      }
    }

    g_numDamageRegions[ class ]++;
    count++;
  }
}


/*
============
G_CalcDamageModifier
============
*/
static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *attacker, int class, int dflags )
{
  vec3_t  targOrigin;
  vec3_t  bulletPath;
  vec3_t  bulletAngle;
  vec3_t  pMINUSfloor, floor, normal;

  float   clientHeight, hitRelative, hitRatio;
  int     bulletRotation, clientRotation, hitRotation;
  float   modifier = 1.0f;
  int     i, j;

  if( point == NULL )
    return 1.0f;

  if( g_unlagged.integer && targ->client && targ->client->unlaggedCalc.used )
    VectorCopy( targ->client->unlaggedCalc.origin, targOrigin );
  else
    VectorCopy( targ->r.currentOrigin, targOrigin );

  clientHeight = targ->r.maxs[ 2 ] - targ->r.mins[ 2 ];

  if( targ->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING )
    VectorCopy( targ->client->ps.grapplePoint, normal );
  else
    VectorSet( normal, 0, 0, 1 );

  VectorMA( targOrigin, targ->r.mins[ 2 ], normal, floor );
  VectorSubtract( point, floor, pMINUSfloor );

  hitRelative = DotProduct( normal, pMINUSfloor ) / VectorLength( normal );

  if( hitRelative < 0.0f )
    hitRelative = 0.0f;

  if( hitRelative > clientHeight )
    hitRelative = clientHeight;

  hitRatio = hitRelative / clientHeight;

  VectorSubtract( targOrigin, point, bulletPath );
  vectoangles( bulletPath, bulletAngle );

  clientRotation = targ->client->ps.viewangles[ YAW ];
  bulletRotation = bulletAngle[ YAW ];

  hitRotation = abs( clientRotation - bulletRotation );

  hitRotation = hitRotation % 360; // Keep it in the 0-359 range

  if( dflags & DAMAGE_NO_LOCDAMAGE )
  {
    for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
    {
      float totalModifier = 0.0f;
      float averageModifier = 1.0f;

      //average all of this upgrade's armour regions together
      if( BG_InventoryContainsUpgrade( i, targ->client->ps.stats ) )
      {
        for( j = 0; j < g_numArmourRegions[ i ]; j++ )
          totalModifier += g_armourRegions[ i ][ j ].modifier;

        if( g_numArmourRegions[ i ] )
          averageModifier = totalModifier / g_numArmourRegions[ i ];
        else
          averageModifier = 1.0f;
      }

      modifier *= averageModifier;
    }
  }
  else
  {
	if (attacker->client)
	{
		attacker->client->pers.statscounters.hitslocational++;
		level.alienStatsCounters.hitslocational++;
	}
    for( i = 0; i < g_numDamageRegions[ class ]; i++ )
    {
      qboolean rotationBound;

      if( g_damageRegions[ class ][ i ].minAngle >
          g_damageRegions[ class ][ i ].maxAngle )
      {
        rotationBound = ( hitRotation >= g_damageRegions[ class ][ i ].minAngle &&
                          hitRotation <= 360 ) || ( hitRotation >= 0 &&
                          hitRotation <= g_damageRegions[ class ][ i ].maxAngle );
      }
      else
      {
        rotationBound = ( hitRotation >= g_damageRegions[ class ][ i ].minAngle &&
                          hitRotation <= g_damageRegions[ class ][ i ].maxAngle );
      }

      if( rotationBound &&
          hitRatio >= g_damageRegions[ class ][ i ].minHeight &&
          hitRatio <= g_damageRegions[ class ][ i ].maxHeight &&
          ( g_damageRegions[ class ][ i ].crouch ==
            ( targ->client->ps.pm_flags & PMF_DUCKED ) ) )
        modifier *= g_damageRegions[ class ][ i ].modifier;
    }    
    
    if(attacker->client && modifier == 2)
    {
     attacker->client->pers.statscounters.headshots++;
     level.alienStatsCounters.headshots++;
    }

    for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
    {
      if( BG_InventoryContainsUpgrade( i, targ->client->ps.stats ) )
      {
        for( j = 0; j < g_numArmourRegions[ i ]; j++ )
        {
          qboolean rotationBound;

          if( g_armourRegions[ i ][ j ].minAngle >
              g_armourRegions[ i ][ j ].maxAngle )
          {
            rotationBound = ( hitRotation >= g_armourRegions[ i ][ j ].minAngle &&
                              hitRotation <= 360 ) || ( hitRotation >= 0 &&
                              hitRotation <= g_armourRegions[ i ][ j ].maxAngle );
          }
          else
          {
            rotationBound = ( hitRotation >= g_armourRegions[ i ][ j ].minAngle &&
                              hitRotation <= g_armourRegions[ i ][ j ].maxAngle );
          }

          if( rotationBound &&
              hitRatio >= g_armourRegions[ i ][ j ].minHeight &&
              hitRatio <= g_armourRegions[ i ][ j ].maxHeight &&
              ( g_armourRegions[ i ][ j ].crouch ==
                ( targ->client->ps.pm_flags & PMF_DUCKED ) ) )
            modifier *= g_armourRegions[ i ][ j ].modifier;
        }
      }
    }
  }

  return modifier;
}


/*
============
G_InitDamageLocations
============
*/
void G_InitDamageLocations( void )
{
  char          *modelName;
  char          filename[ MAX_QPATH ];
  int           i;
  int           len;
  fileHandle_t  fileHandle;
  char          buffer[ MAX_LOCDAMAGE_TEXT ];

  for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
  {
    modelName = BG_FindModelNameForClass( i );
    Com_sprintf( filename, sizeof( filename ), "models/players/%s/locdamage.cfg", modelName );

    len = trap_FS_FOpenFile( filename, &fileHandle, FS_READ );
    if ( !fileHandle )
    {
      G_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
      continue;
    }

    if( len >= MAX_LOCDAMAGE_TEXT )
    {
      G_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_LOCDAMAGE_TEXT ) );
      trap_FS_FCloseFile( fileHandle );
      continue;
    }

    trap_FS_Read( buffer, len, fileHandle );
    buffer[len] = 0;
    trap_FS_FCloseFile( fileHandle );

    G_ParseDmgScript( buffer, i );
  }

  for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
  {
    modelName = BG_FindNameForUpgrade( i );
    Com_sprintf( filename, sizeof( filename ), "armour/%s.armour", modelName );

    len = trap_FS_FOpenFile( filename, &fileHandle, FS_READ );

    //no file - no parsage
    if ( !fileHandle )
      continue;

    if( len >= MAX_LOCDAMAGE_TEXT )
    {
      G_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_LOCDAMAGE_TEXT ) );
      trap_FS_FCloseFile( fileHandle );
      continue;
    }

    trap_FS_Read( buffer, len, fileHandle );
    buffer[len] = 0;
    trap_FS_FCloseFile( fileHandle );

    G_ParseArmourScript( buffer, i );
  }
}

////////TA: locdamage


/*
============
T_Damage

targ    entity that is being damaged
inflictor entity that is causing the damage
attacker  entity that caused the inflictor to damage targ
  example: targ=monster, inflictor=rocket, attacker=player

dir     direction of the attack for knockback
point   point at which the damage is being inflicted, used for headshots
damage    amount of damage being inflicted
knockback force to be applied against targ as a result of the damage

inflictor, attacker, dir, and point can be NULL for environmental effects

dflags    these flags are used to control how T_Damage works
  DAMAGE_RADIUS     damage was indirect (from a nearby explosion)
  DAMAGE_NO_ARMOR     armor does not protect from this damage
  DAMAGE_NO_KNOCKBACK   do not affect velocity, just view angles
  DAMAGE_NO_PROTECTION  kills godmode, armor, everything
============
*/

//TA: team is the team that is immune to this damage
void G_SelectiveDamage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
         vec3_t dir, vec3_t point, int damage, int dflags, int mod, int team )
{
  if( targ->client && ( team != targ->client->ps.stats[ STAT_PTEAM ] || targ->client->pers.bleeder ) )
    G_Damage( targ, inflictor, attacker, dir, point, damage, dflags, mod );
}

void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
         vec3_t dir, vec3_t point, int damage, int dflags, int mod )
{
  gclient_t *client;
  int     take;
  int     save;
  int     asave = 0;
  int     knockback;
  int     takeNoOverkill;
  float damagemodifier=0.0;

  if( !targ->takedamage )
    return;

  // the intermission has allready been qualified for, so don't
  // allow any extra scoring
  if( level.intermissionQueued )
    return;

  if( !inflictor )
    inflictor = &g_entities[ ENTITYNUM_WORLD ];

  if( !attacker )
    attacker = &g_entities[ ENTITYNUM_WORLD ];
  else if( attacker->client && attacker->client->pers.paused )
    return;

  // shootable doors / buttons don't actually have any health
  if( targ->s.eType == ET_MOVER )
  {
    if( targ->use && ( targ->moverState == MOVER_POS1 ||
                       targ->moverState == ROTATOR_POS1 ) )
      targ->use( targ, inflictor, attacker );

    return;
  }

  // don't attack buildable when noBaseAttack, you can't destroy them
  if (g_noBaseAttack.integer && targ->s.eType == ET_BUILDABLE)
     return;

  client = targ->client;

  if( client )
  {
    if( client->noclip && !g_devmapNoGod.integer)
      return;
      if( client->pers.paused )
      return;
  }

  if( !dir )
    dflags |= DAMAGE_NO_KNOCKBACK;
  else
    VectorNormalize( dir );

  knockback = damage;

  if( inflictor->s.weapon != WP_NONE )
  {
    knockback = (int)( (float)knockback *
      BG_FindKnockbackScaleForWeapon( inflictor->s.weapon ) );
  }

  // ...and for goon pouncing
  if( mod == MOD_LEVEL3_POUNCE )
    knockback *= 1.5f;

  if( targ->client )
  {
    knockback = (int)( (float)knockback *
      BG_FindKnockbackScaleForClass( targ->client->ps.stats[ STAT_PCLASS ] ) );
  }

  if( knockback > 200 )
    knockback = 200;

  if( level.instaGibMode && inflictor->s.weapon == WP_MASS_DRIVER )
    knockback = 1000;

  if( targ->flags & FL_NO_KNOCKBACK )
    knockback = 0;

  if( dflags & DAMAGE_NO_KNOCKBACK )
    knockback = 0;

  //ROTAX
  if ( g_ambush.integer && g_ambush_kill_spawns.integer == 0 &&
	  ROTACAK_ambush_stage != 9 && targ->s.modelindex == BA_A_SPAWN )
    return;//dont kill eggs

  // figure momentum add, even if the damage won't be taken
  if( knockback && targ->client )
  {
    vec3_t  kvel;
    float   mass;

    mass = 200;

    VectorScale( dir, g_knockback.value * (float)knockback / mass, kvel );
    VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity );

    // set the timer so that the other client can't cancel
    // out the movement immediately
    if( !targ->client->ps.pm_time )
    {
      int   t;

      t = knockback * 2;
      if( t < 50 )
        t = 50;

      if( t > 200 )
        t = 200;

      targ->client->ps.pm_time = t;
      targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
    }
  }

  // check for completely getting out of the damage
  if( !( dflags & DAMAGE_NO_PROTECTION ) )
  {

    // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target
    // if the attacker was on the same team
    if( targ != attacker && OnSameTeam( targ, attacker ) )
    {
      if( g_dretchPunt.integer &&
        targ->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL0 )
      {
        vec3_t dir, push;

        VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, dir );
        VectorNormalizeFast( dir );
        VectorScale( dir, ( damage * 10.0f ), push );
        push[2] = 64.0f;
        VectorAdd( targ->client->ps.velocity, push, targ->client->ps.velocity );
        return;
	  } else if( !g_friendlyFireMovementAttacks.integer && (mod == MOD_LEVEL4_CHARGE || mod == MOD_LEVEL4_CRUSH || mod == MOD_LEVEL3_POUNCE )) {
	       // don't do friendly fire on movement attacks
	       return;
      }
      else if( !g_friendlyFire.integer )
      {
        if( !g_friendlyFireHumans.integer 
          && targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
        {
          return;
        }
        if( !g_friendlyFireAliens.integer 
          && targ->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
        {
          return;
        }
      }
    }

		// If target is buildable on the same team as the attacking client
		if( targ->s.eType == ET_BUILDABLE && attacker->client &&
				targ->biteam == attacker->client->pers.teamSelection )
		{
			if( !g_friendlyBuildableFire.integer )
				return;
			// don't do friendly fire on movement attacks
			 if( !g_friendlyFireMovementAttacks.integer && (mod == MOD_LEVEL4_CHARGE || mod == MOD_LEVEL4_CRUSH || mod == MOD_LEVEL3_POUNCE ))
			return;
		}

    // check for godmode
    if ( (targ->flags & FL_GODMODE && !g_devmapNoGod.integer) || ( (level.time - targ->client->respawnTime) < g_instaGibGod.integer*1000 && level.instaGibMode ) )
      return;

    // paused
    if ( level.paused )
      return;
    
    if(targ->s.eType == ET_BUILDABLE && g_cheats.integer && g_devmapNoStructDmg.integer)
	    return;
  }

  // add to the attacker's hit counter
  if( attacker->client && targ != attacker && targ->health > 0
      && targ->s.eType != ET_MISSILE
      && targ->s.eType != ET_GENERAL )
  {
    if( OnSameTeam( targ, attacker ) )
      attacker->client->ps.persistant[ PERS_HITS ]--;
    else
      attacker->client->ps.persistant[ PERS_HITS ]++;
  }

  take = damage;
  save = 0;

  // add to the damage inflicted on a player this frame
  // the total will be turned into screen blends and view angle kicks
  // at the end of the frame
  if( client )
  {
    if( attacker )
      client->ps.persistant[ PERS_ATTACKER ] = attacker->s.number;
    else
      client->ps.persistant[ PERS_ATTACKER ] = ENTITYNUM_WORLD;

    client->damage_armor += asave;
    client->damage_blood += take;
    client->damage_knockback += knockback;

    if( dir )
    {
      VectorCopy ( dir, client->damage_from );
      client->damage_fromWorld = qfalse;
    }
    else
    {
      VectorCopy ( targ->r.currentOrigin, client->damage_from );
      client->damage_fromWorld = qtrue;
    }

    // set the last client who damaged the target
    targ->client->lasthurt_client = attacker->s.number;
    targ->client->lasthurt_mod = mod;
    
    damagemodifier = G_CalcDamageModifier( point, targ, attacker, client->ps.stats[ STAT_PCLASS ], dflags );
    take = (int)( (float)take * damagemodifier );

    //if boosted poison every attack
    if( attacker->client && attacker->client->ps.stats[ STAT_STATE ] & SS_BOOSTED )
    {
      if( targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
          !( targ->client->ps.stats[ STAT_STATE ] & SS_POISONED ) &&
          mod != MOD_LEVEL2_ZAP &&
          targ->client->poisonImmunityTime < level.time )
      {
        targ->client->ps.stats[ STAT_STATE ] |= SS_POISONED;
        targ->client->lastPoisonTime = level.time;
        targ->client->lastPoisonClient = attacker;
		attacker->client->pers.statscounters.repairspoisons++;
		level.alienStatsCounters.repairspoisons++;
      }
    }
	if ( mod == MOD_SLOWBLOB && attacker->client->pers.classSelection == PCL_ALIEN_BUILDER0_UPG )
 	     targ->client->lastSpatAtTime = level.time;
  }

  if( take < 1 )
    take = 1;

  if( g_debugDamage.integer )
  {
    G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number,
      targ->health, take, asave );
  }


  if( take )
  {
    if( take > targ->health ) 
    {
      take = targ->health;
    }
    
    //Increment some stats counters
    if( attacker->client )
    {
      if( targ->biteam == attacker->client->pers.teamSelection || OnSameTeam( targ, attacker ) ) 
      {
		attacker->client->pers.statscounters.ffdmgdone += take;
		if( attacker->client->pers.teamSelection == PTE_ALIENS ) 
		{
			level.alienStatsCounters.ffdmgdone+=take;
		}
		else if( attacker->client->pers.teamSelection == PTE_HUMANS )
		{
			level.humanStatsCounters.ffdmgdone+=take;
		}

        // bleeding spree
        if( g_bleedingSpree.integer > 1 && attacker != targ && !level.instaGibMode )
        {
          attacker->client->pers.statscounters.spreebleeds += take;
          if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 100 &&
              !attacker->client->pers.bleeder )
          {
            attacker->client->pers.bleeder = qtrue;
            level.bleeders++;
            trap_SendServerCommand( -1,
              va( "print \"%s^3 has become an enemy of their own base\n\"",
              attacker->client->pers.netname ) );
            trap_SendServerCommand( attacker - g_entities,
              "cp \"^1Your team bleeding has irritated your base!\"" );

            G_admin_tklog_log( attacker, NULL, mod );
          }
          else if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 66 &&
                  !attacker->client->pers.bleeder &&
                   attacker->client->pers.bleederLastWarn + 20 * 1000 < level.time )
          {
            attacker->client->pers.bleederLastWarn = level.time;
            trap_SendServerCommand( attacker - g_entities,
              "print \"^3Your bleeding is close to aggravating your base!\n\"" );
            trap_SendServerCommand( attacker - g_entities,
              "cp \"^1Your bleeding is close to aggravating your base!\"" );
          }
        }
      }
      else if( targ->s.eType == ET_BUILDABLE )
      {
		attacker->client->pers.statscounters.structdmgdone += take;
	      
		if( attacker->client->pers.teamSelection == PTE_ALIENS ) 
		{
			level.alienStatsCounters.structdmgdone+=take;
		}
		else if( attacker->client->pers.teamSelection == PTE_HUMANS )
		{
			level.humanStatsCounters.structdmgdone+=take;
		}
	      
		if( targ->health > 0 && ( targ->health - take ) <=0 )
		{
			attacker->client->pers.statscounters.structskilled++;
			if( attacker->client->pers.teamSelection == PTE_ALIENS ) 
			{
				level.alienStatsCounters.structskilled++;
			}
			else if( attacker->client->pers.teamSelection == PTE_HUMANS )
			{
				level.humanStatsCounters.structskilled++;
			}
		}

        if( attacker->client->pers.statscounters.spreefeeds )
        {
          attacker->client->pers.statscounters.spreefeeds -= take;
          if( attacker->client->pers.statscounters.spreefeeds < 0 )
            attacker->client->pers.statscounters.spreefeeds = 0;
        }

        if( attacker->client->pers.statscounters.spreebleeds > 10 )
        {
          attacker->client->pers.statscounters.spreebleeds -= take / 3;
          if( attacker->client->pers.statscounters.spreebleeds < 10 )
            attacker->client->pers.statscounters.spreebleeds = 10;
        }
      }
      else if( targ->client )
      {
		attacker->client->pers.statscounters.dmgdone +=take;
        attacker->client->pers.statscounters.hits++; 	      
		if( attacker->client->pers.teamSelection == PTE_ALIENS ) 
		{
			level.alienStatsCounters.dmgdone+=take;
		}
		else if( attacker->client->pers.teamSelection == PTE_HUMANS )
		{
			level.humanStatsCounters.dmgdone+=take;
		}

        if( attacker->client->pers.statscounters.spreefeeds )
        {
          attacker->client->pers.statscounters.spreefeeds -= take;
          if( attacker->client->pers.statscounters.spreefeeds < 0 )
            attacker->client->pers.statscounters.spreefeeds = 0;
        }

        if( attacker->client->pers.statscounters.spreebleeds > 10 )
        {
          attacker->client->pers.statscounters.spreebleeds -= take / 3;
          if( attacker->client->pers.statscounters.spreebleeds < 10 )
            attacker->client->pers.statscounters.spreebleeds = 10;
        }
      }	
    }

    
    takeNoOverkill = take;
    if( takeNoOverkill > targ->health ) 
    {
      takeNoOverkill = targ->health;
    }
    
    targ->health = targ->health - take;   // do the damage

    if( targ->client )
      targ->client->ps.stats[ STAT_HEALTH ] = targ->health;

    targ->lastDamageTime = level.time;

    //TA: add to the attackers "account" on the target
    if( targ->client && attacker->client )
    {
      if( attacker != targ && !OnSameTeam( targ, attacker ) )
        targ->credits[ attacker->client->ps.clientNum ] += take;
	  else if( attacker != targ && OnSameTeam( targ, attacker ) )
        targ->client->tkcredits[ attacker->client->ps.clientNum ] += takeNoOverkill;
    }

    if( targ->health <= 0 )
    {
      if( client )
        targ->flags |= FL_NO_KNOCKBACK;

      if( targ->health < -999 )
        targ->health = -999;

      targ->enemy = attacker;
      targ->die( targ, inflictor, attacker, take, mod );
      return;
    }
    else if( targ->pain )
      targ->pain( targ, attacker, take );
  }
}


/*
============
CanDamage

Returns qtrue if the inflictor can directly damage the target.  Used for
explosions and melee attacks.
============
*/
qboolean CanDamage( gentity_t *targ, vec3_t origin )
{
  vec3_t  dest;
  trace_t tr;
  vec3_t  midpoint;

  // use the midpoint of the bounds instead of the origin, because
  // bmodels may have their origin is 0,0,0
  VectorAdd( targ->r.absmin, targ->r.absmax, midpoint );
  VectorScale( midpoint, 0.5, midpoint );

  VectorCopy( midpoint, dest );
  trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
  if( tr.fraction == 1.0  || tr.entityNum == targ->s.number )
    return qtrue;

  // this should probably check in the plane of projection,
  // rather than in world coordinate, and also include Z
  VectorCopy( midpoint, dest );
  dest[ 0 ] += 15.0;
  dest[ 1 ] += 15.0;
  trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
  if( tr.fraction == 1.0 )
    return qtrue;

  VectorCopy( midpoint, dest );
  dest[ 0 ] += 15.0;
  dest[ 1 ] -= 15.0;
  trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
  if( tr.fraction == 1.0 )
    return qtrue;

  VectorCopy( midpoint, dest );
  dest[ 0 ] -= 15.0;
  dest[ 1 ] += 15.0;
  trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
  if( tr.fraction == 1.0 )
    return qtrue;

  VectorCopy( midpoint, dest );
  dest[ 0 ] -= 15.0;
  dest[ 1 ] -= 15.0;
  trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
  if( tr.fraction == 1.0 )
    return qtrue;

  return qfalse;
}


//TA:
/*
============
G_SelectiveRadiusDamage
============
*/
qboolean G_SelectiveRadiusDamage( vec3_t origin, gentity_t *attacker, float damage,
                                  float radius, gentity_t *ignore, int mod, int team )
{
  float     points, dist;
  gentity_t *ent;
  int       entityList[ MAX_GENTITIES ];
  int       numListedEntities;
  vec3_t    mins, maxs;
  vec3_t    v;
  vec3_t    dir;
  int       i, e;
  qboolean  hitClient = qfalse;

  if( level.intermissiontime )
	  return qfalse;

  if( radius < 1 )
    radius = 1;

  for( i = 0; i < 3; i++ )
  {
    mins[ i ] = origin[ i ] - radius;
    maxs[ i ] = origin[ i ] + radius;
  }

  numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );

  for( e = 0; e < numListedEntities; e++ )
  {
    ent = &g_entities[ entityList[ e ] ];

    if( ent == ignore )
      continue;

    if( !ent->takedamage )
      continue;

    // find the distance from the edge of the bounding box
    for( i = 0 ; i < 3 ; i++ )
    {
      if( origin[ i ] < ent->r.absmin[ i ] )
        v[ i ] = ent->r.absmin[ i ] - origin[ i ];
      else if( origin[ i ] > ent->r.absmax[ i ] )
        v[ i ] = origin[ i ] - ent->r.absmax[ i ];
      else
        v[ i ] = 0;
    }

    dist = VectorLength( v );
    if( dist >= radius )
      continue;

    points = damage * ( 1.0 - dist / radius );

    if( CanDamage( ent, origin ) )
    {
      VectorSubtract( ent->r.currentOrigin, origin, dir );
      // push the center of mass higher than the origin so players
      // get knocked into the air more
      dir[ 2 ] += 24;
      G_SelectiveDamage( ent, NULL, attacker, dir, origin,
          (int)points, DAMAGE_RADIUS|DAMAGE_NO_LOCDAMAGE, mod, team );
    }
  }

  return hitClient;
}


/*
============
G_RadiusDamage
============
*/
qboolean G_RadiusDamage( vec3_t origin, gentity_t *attacker, float damage,
                         float radius, gentity_t *ignore, int mod )
{
  float     points, dist;
  gentity_t *ent;
  int       entityList[ MAX_GENTITIES ];
  int       numListedEntities;
  vec3_t    mins, maxs;
  vec3_t    v;
  vec3_t    dir;
  int       i, e;
  qboolean  hitClient = qfalse;
  trace_t   tr;

  if( radius < 1 )
    radius = 1;

  for( i = 0; i < 3; i++ )
  {
    mins[ i ] = origin[ i ] - radius;
    maxs[ i ] = origin[ i ] + radius;
  }

  numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );

  for( e = 0; e < numListedEntities; e++ )
  {
    ent = &g_entities[ entityList[ e ] ];

    if( ent == ignore )
      continue;

    if( !ent->takedamage )
      continue;

    // find the distance from the edge of the bounding box
    for( i = 0; i < 3; i++ )
    {
      if( origin[ i ] < ent->r.absmin[ i ] )
        v[ i ] = ent->r.absmin[ i ] - origin[ i ];
      else if( origin[ i ] > ent->r.absmax[ i ] )
        v[ i ] = origin[ i ] - ent->r.absmax[ i ];
      else
        v[ i ] = 0;
    }

    dist = VectorLength( v );
    if( dist >= radius )
      continue;

    //// need line of sight
    //trap_Trace( &tr, origin, NULL, NULL, ent->s.origin, attacker-g_entities,
    //  MASK_SHOT );
    //if( tr.entityNum != ent-g_entities )
    //  continue;

    points = damage * ( 1.0 - dist / radius );

    if( CanDamage( ent, origin ) )
    {
      VectorSubtract( ent->r.currentOrigin, origin, dir );
      // push the center of mass higher than the origin so players
      // get knocked into the air more
      dir[ 2 ] += 24;
      G_Damage( ent, NULL, attacker, dir, origin,
          (int)points, DAMAGE_RADIUS|DAMAGE_NO_LOCDAMAGE, mod );
    }
  }

  return hitClient;
}

void G_Knockback( gentity_t *targ, vec3_t dir, int knockback )
{
  if( knockback && targ->client )
  {
    vec3_t  kvel;
    float   mass;

    mass = 200;

    // Halve knockback for bsuits
    if( targ->client &&
        targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
        BG_InventoryContainsUpgrade( UP_BATTLESUIT, targ->client->ps.stats ) )
      mass += 400;

    // Halve knockback for crouching players
    if(targ->client->ps.pm_flags&PMF_DUCKED) knockback /= 2;

    VectorScale( dir, g_knockback.value * (float)knockback / mass, kvel );
    VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity );

    // set the timer so that the other client can't cancel
    // out the movement immediately
    if( !targ->client->ps.pm_time )
    {
      int   t;

      t = knockback * 2;
      if( t < 50 )
        t = 50;

      if( t > 200 )
        t = 200;
      targ->client->ps.pm_time = t;
      targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
    }
  }
}


//ROTAX
void kill_aliens()
{
  int       i;
  gentity_t *ent;

  for( i = 0; i < level.numConnectedClients; i++ )
  {
    ent = &g_entities[ i ];
    
    if( ent->health > 0 && ent->client->pers.teamSelection == PTE_ALIENS)
    {
      G_Damage( ent, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
    }
  }
}

//ROTAX
void ambush_next_stage()
{
  char    string[ 1024 ];
  int     min, tens, sec;
  int increment = g_ambush_rebuild_time.integer * 1000;

  ROTACAK_ambush_stage++;

  if (g_ambush_stage_suicide.integer == 1)
    kill_aliens();

  sec = ((level.time - level.startTime) + increment) / 1000;

  min = sec / 60;
  sec -= min * 60;
  tens = sec / 10;
  sec -= tens * 10;

  Com_sprintf( string, sizeof( string ), "%i:%i%i", min, tens, sec );

  trap_SendServerCommand( -1, va( "cp \"^1--> Prepare for next wave (^2Stage %i^1) <--\n^1(attack in %s)^7\"", ROTACAK_ambush_stage, string ) );
  trap_SendServerCommand( -1, va( "print \"^1--> Prepare for next wave (^2Stage %i^1) <--\n^1(attack in %s)^7\n\"", ROTACAK_ambush_stage, string ) );
  
  ROTACAK_ambush_rebuild_time_temp = level.time + increment;
}

